﻿using System;
using System.Collections.Generic;
using System.Text;
using SMBL.Interface.LifeCycle;
using SMBL.Interface.Database;
using SMBL.Global;
using SMBL.ConfigElement.DBProvider;
using SMBL.Core;
using SMBL.ADBS.Providers;

namespace SMBL
{
    namespace ADBS
    {
        ///
        /// <summary>
        /// Connection Pool
        /// @Author: Push
        /// </summary>
        public class ConnectionPool : IStart, IEnd
        {
            //////////////////////////////////////////////////////////////////////////
            // Parameters
            //////////////////////////////////////////////////////////////////////////

            // Connection String
            private String myConnString;

            // Provider information
            private DBProviderInfo myProviderInfo;
            // My Name
            public String Name
            {
                get { return myProviderInfo.Name; }
            }
            // The number of current connections
            private Int32 myNowConnections = 0;
            // The Last Request Id
            private Int32 myLastGetConnID = 0;
            // Current Request Id
            private Int32 myCurrentWaitingID = 0;

            // Provider Pool
            private Stack<IDBProvider> myConnPool;

            // Lock Object
            private static object lockObj = new object();

            /// <summary>
            /// Construction of ConnectionPool
            /// @Author: Push
            /// </summary>
            /// <param name="ProviderName"></param>
            public ConnectionPool(DBProviderInfo ProviderName)
            {
                // Initialize my Provider Information
                myProviderInfo = ProviderName;
            }

            //////////////////////////////////////////////////////////////////////////
            // Interface
            //////////////////////////////////////////////////////////////////////////

            #region IStart Members

            /// <summary>
            /// Start the connection pool server
            /// Bind With the DBprovider
            /// @Author: Push
            /// </summary>
            /// <returns>The statue of the process</returns>
            public bool Start()
            {
                lock (lockObj)
                {
                    // If MaxConn is no larger than 0, than there will be some error
                    if (myProviderInfo.MaxConn <= 0)
                    {
                        ErrorSystem.CatchError("The DBProviderInfo: " + myProviderInfo.Name +
                            " has illegal Max Connection Amount.");
                        return false;
                    }
                    // Get Information from the specified DBProviderInfo
                    myConnPool = new Stack<IDBProvider>();
                    for (Int64 i = myProviderInfo.MaxConn; i != 0; --i)
                    {
                        myConnPool.Push(
                            (IDBProvider)Activator.CreateInstance(
                                Type.GetType(
                                    "SMBL.ADBS.Providers." + myProviderInfo.TYPE.ToString()
                                    )
                                )
                            );
                    }
                }
                // Generate the connection
                this.generateConnectString();
                return true;
            }

            #endregion

            #region IEnd Members

            /// <summary>
            /// End the Connection Pool Server
            /// Release the resource
            /// @Author; Push
            /// </summary>
            /// <returns>The statue of the process</returns>
            public bool End()
            {
                lock (lockObj)
                {
                    // Disconnect from the database
                    for (Int64 i = myProviderInfo.MaxConn; i != 0; --i)
                    {
                        IDBProvider tmpProvider = myConnPool.Pop();
                        //tmpProvider.DisConnect();
                        tmpProvider.End();
                    }
                    // Release the data
                    myConnPool.Clear();
                }

                return true;
            }

            #endregion

            //////////////////////////////////////////////////////////////////////////
            // Methods
            //////////////////////////////////////////////////////////////////////////

            /// <summary>
            /// Generate the connection string
            /// @Author: Push
            /// </summary>
            private void generateConnectString()
            {
                if (myConnString != null) return;
                ConnectString tmpConnString = (ConnectString)Kernel.GetInstance().ConnectStringList[myProviderInfo.TYPE];

                // Has BUG here, the function does not check the database type.
                // The function should check if the type is SQLDB or OLEDB or other.
                // One database should link to one generate connection string function.
                // The function should be offered by the provider.
                /*********************************************************************/
                if (tmpConnString != null)
                {
                    myConnString = ("Data Source=" + myProviderInfo.DBPath.Trim() + ";") + tmpConnString.InnerText;
                    myConnString += myProviderInfo.DBName.Trim();
                    myConnString += ";User ID=" + myProviderInfo.UserName.Trim() + 
                        ";Password=" + myProviderInfo.Password.Trim();
                }
                /*********************************************************************/
            }

            /// <summary>
            /// Get a provider
            /// @Author: Push
            /// </summary>
            /// <returns>A Provider Instance with specified TYPE</returns>
            public IDBProvider GetConnection()
            {
                IDBProvider tmpProvider;
                Int32 tmpWaitingID;
                lock (lockObj)
                {
                    tmpWaitingID = ++myCurrentWaitingID;
                }
                while ((myNowConnections == myProviderInfo.MaxConn)
                    || (myLastGetConnID != (tmpWaitingID - 1))) ;
                lock (lockObj)
                {
                    ++myLastGetConnID;
                    ++myNowConnections;
                    // Create the Provider by the provider type.
                    tmpProvider = myConnPool.Pop();
                }
                // Set the connection string
                tmpProvider.SetConnectionString(myConnString);
                return tmpProvider;
            }

            /// <summary>
            /// Release current provider
            /// @Author: Push
            /// </summary>
            /// <param name="UsedProvider"></param>
            public void ReleaseConnection(IDBProvider UsedProvider)
            {
                // Release the connection of the database.
                // UsedProvider.DisConnect();
                --myNowConnections;
                myConnPool.Push(UsedProvider);
            }
        }
    }
}